package com.google.gwt.gwtpages.client.page;

import java.util.Stack;

import com.google.gwt.gwtpages.client.GotoPageCommand;
import com.google.gwt.gwtpages.client.PageRequestSession;
import com.google.gwt.gwtpages.client.Pages;
import com.google.gwt.gwtpages.client.page.loader.PageLoader;
import com.google.gwt.gwtpages.client.page.parameters.PageParameters;

/**
 * Page event handler used to keep a user session-based page stack.  Normally, pages are added to the
 * stack when the page is entered and removed from the stack on exit.  Pages can override this
 * functionality by implementing {@link PageStackAware}.  This is not a default page event handler
 * and must be added manually to the {@link Pages} instance using {@link Pages#add(PageEventHandler...)}
 * 
 * @author Joe Hudson
 */
public class PageStackEventHandler implements PageEventHandler {

	private Stack<PageStackItem> pageStack = new Stack<PageStackItem>();

	public void onAfterPageEnter(LoadedPageContainer pageLoadResult,
			PageParameters parameters, GotoPageCommand command) {
		boolean keepPageInStack = false;
		if (pageLoadResult.getPage() instanceof PageStackAware) {
			keepPageInStack = ((PageStackAware) pageLoadResult.getPage())
					.keepInPageStack(pageLoadResult, parameters, command);
		}
		if (pageStack.size() > 0) {
			if (!pageStack.peek().shouldKeepInStack())
				pageStack.pop();
		}
		pageStack.push(new PageStackItem(command, pageLoadResult, parameters,
				keepPageInStack));
	}

	public void onPageEnterSuccess(LoadedPageContainer pageLoadResult,
			PageParameters parameters, GotoPageCommand command) {
	}

	public void onPageEnterFailure(LoadedPageContainer pageLoadResult,
			PageParameters parameters, GotoPageCommand command) {
	}

	public void onPageRequest(String pageToken, String historyToken,
			PageRequestSession session) {
	}

	public void onBeforePageEnter(LoadedPageContainer pageLoadResult,
			PageParameters parameters, GotoPageCommand command) {
	}

	public void onPageLoaded(LoadedPageContainer result) {
	}

	public void onPageNotFound(String historyToken) {
	}

	public void onPageLoadFailure(String historyToken, Throwable cause) {
	}

	public void onPageWaitForAsync(LoadedPageContainer loadedPageContainer,
			PageParameters parameters, GotoPageCommand command) {
	}

	/**
	 * Container class for page-related information related to a single page
	 * request that is persisted in the page stack. The
	 * {@link PageLoader#keepInStack(LoadedPageContainer, PageParameters)} is
	 * used to determine if the page should be kept in the page stack even after
	 * another page is requested.
	 * 
	 * @author Joe Hudson
	 */
	public class PageStackItem {

		private LoadedPageContainer loadedPageContainer;
		private PageParameters parameters;
		private GotoPageCommand command;
		private boolean keepInStack;

		public PageStackItem(GotoPageCommand command,
				LoadedPageContainer loadedPageContainer,
				PageParameters parameters, boolean keepInStack) {
			this.command = command;
			this.loadedPageContainer = loadedPageContainer;
			this.parameters = parameters;
		}

		/**
		 * Return the {@link LoadedPageContainer} containing all of the
		 * associated page data
		 */
		public LoadedPageContainer getLoadedPageContainer() {
			return loadedPageContainer;
		}

		/**
		 * Return the {@link PageParameters} for the specific page request
		 */
		public PageParameters getParameters() {
			return parameters;
		}

		/**
		 * Return true if this page should be kept in the page stack and false
		 * if not. See {@link Pages#getPageStack()}
		 */
		public boolean shouldKeepInStack() {
			return keepInStack;
		}

		/**
		 * Return a command that will navigate to this page
		 */
		public GotoPageCommand getCommand() {
			return command;
		}
	}

	public interface PageStackAware {

		/**
		 * Return true to keep the page in the stack and false to remove it
		 * 
		 * @return
		 */
		public boolean keepInPageStack(LoadedPageContainer pageLoadResult,
				PageParameters parameters, GotoPageCommand command);
	}
}